import { SpriteFrame, Prefab, _decorator, color, director, System, AudioClip } from 'cc';
import { BaseManager } from '../../scripts/libs/rxjs/cc3/BaseManager';
import { bundleLoad, bundleLoadDir } from '../libs/rxjs/cc3/AssetManagerLoad';
import { concat, map } from 'rxjs';
import { ReactiveProperty } from '../libs/rxjs/cc3/ReactiveProperty';
import { assetManager } from 'cc';
import { ReactiveCommand } from '../libs/rxjs/cc3/ReactiveCommand';
import { SpriteAtlas } from 'cc';
const { ccclass, property } = _decorator;

/**
 * 资源类型
 */
export enum ResType {
    Unknown = 'Unknown',
    Prefab = 'Prefab',
    SpriteFrame = 'SpriteFrame',
    SpriteAtlas = 'SpriteAtlas',
    AudioClip = 'AudioClip',
    AudioClipDir = 'AudioClipDir',
}

@ccclass('ResourceManager')
export class ResourceManager extends BaseManager {

    public static instance: ResourceManager;
    static ID = 'ResourceManager_system';
    get logColorOverride() { return color().fromHEX('#EECCDD') };

    static registerSystem() {
        director.unregisterSystem(director.getSystem(ResourceManager.ID));
        director.registerSystem(ResourceManager.ID, rm, System.Priority.MEDIUM);
        rm.init();
    }

    static unregisterSystem() {
        rm.destroy();
        director.unregisterSystem(director.getSystem(ResourceManager.ID));
    }

    onInitManager() {
        this.log('onInitManager', this.timestamp);
    }

    useObserves() {
        return [
        ];
    }

    prefabs: Record<string, Prefab> = {};
    spriteFrames: Record<string, SpriteFrame> = {};
    spriteAtlas: Record<string, SpriteAtlas> = {};
    audioClips: Record<string, AudioClip> = {};

    finishedCount = ReactiveProperty.Create(0);

    loadRes(resList: string[][]) {
        this.finishedCount.value = 0;
        let obs = [];
        for (let i = 0; i < resList.length; i++) {
            const [resPath, resType] = resList[i];
            let [bundleName, assetPath] = this.splitResPath(resPath);
            switch (resType) {
                case ResType.Prefab:
                    obs.push(bundleLoad(bundleName, assetPath, Prefab, (finished, total) => {
                        this.log('loadRes progress:', resPath, finished, total);
                        this.finishedCount.value++;
                    }).pipe(map(_ => {
                        this.prefabs[resPath] = _.data as Prefab;
                        return `loadRes ${resPath} (type:${resType}) success`;
                    })));
                    break;
                case ResType.SpriteFrame:
                    obs.push(bundleLoad(bundleName, assetPath, SpriteFrame, (finished, total) => {
                        this.log('loadRes progress:', resPath, finished, total);
                        this.finishedCount.value++;
                    }).pipe(map(_ => {
                        this.spriteFrames[resPath] = _.data as SpriteFrame;
                        return `loadRes ${resPath} (type:${resType}) success`;
                    })));
                    break;
                case ResType.SpriteAtlas:
                    obs.push(bundleLoad(bundleName, assetPath, SpriteAtlas, (finished, total) => {
                        this.log('loadRes progress:', resPath, finished, total);
                        this.finishedCount.value++;
                    }).pipe(map(_ => {
                        this.spriteAtlas[resPath] = _.data as SpriteAtlas;
                        return `loadRes ${resPath} (type:${resType}) success`;
                    })));
                    break;
                case ResType.AudioClip:
                    obs.push(bundleLoad(bundleName, assetPath, AudioClip, (finished, total) => {
                        this.log('loadRes progress:', resPath, finished, total);
                        this.finishedCount.value++;
                    }).pipe(map(_ => {
                        this.audioClips[resPath] = _.data as AudioClip;
                        return `loadRes ${resPath} (type:${resType}) success`;
                    })));
                    break;
                case ResType.AudioClipDir:
                    obs.push(bundleLoadDir(bundleName, assetPath, AudioClip, (finished, total) => {
                        this.log('loadRes progress:', resPath, finished, total);
                        this.finishedCount.value++;
                    }).pipe(map(_ => {
                        _.data.forEach((audioClip, idx) => {
                            let key = `${resPath}/${audioClip.name}`;
                            this.audioClips[key] = audioClip;
                        });
                        return `loadRes ${resPath} (type:${resType}) success`;
                    })));
                    break;
                default:
                    break;
            }
        }
        return concat(...obs);
    }

    releaseAudioClipCommand = ReactiveCommand.Create<string>();

    releaseRes(resList: string[][]) {
        for (let i = 0; i < resList.length; i++) {
            const [resPath, resType] = resList[i];
            let [bundleName, assetPath] = this.splitResPath(resPath);
            switch (resType) {
                case ResType.Prefab:
                    if (this.prefabs[resPath]) {
                        delete this.prefabs[resPath];
                        assetManager.getBundle(bundleName).release(assetPath, Prefab);
                        this.log('releaseRes', resPath, resType);
                    }
                    break;
                case ResType.SpriteFrame:
                    if (this.spriteFrames[resPath]) {
                        delete this.spriteFrames[resPath];
                        assetManager.getBundle(bundleName).release(assetPath, SpriteFrame);
                        this.log('releaseRes', resPath, resType);
                    }
                    break;
                case ResType.SpriteAtlas:
                    if (this.spriteAtlas[resPath]) {
                        delete this.spriteAtlas[resPath];
                        assetManager.getBundle(bundleName).release(assetPath, SpriteAtlas);
                        this.log('releaseRes', resPath, resType);
                    }
                    break;
                case ResType.AudioClipDir: // TODO: 没有测试过
                    for (let key in this.audioClips) {
                        if (key.startsWith(resPath)) {
                            assetManager.getBundle(bundleName).release(key, AudioClip);
                            delete this.audioClips[key];
                            this.log('releaseRes', key, resType);
                            this.releaseAudioClipCommand.execute(key);
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }

    splitResPath(resPath: string) {
        let [firstPart, ...rest] = resPath.split("/");
        let secondPart = rest.join("/");
        return [firstPart, secondPart];
    }

}
globalThis.ResourceManager = ResourceManager;

export const rm = ResourceManager.instance = new ResourceManager();